home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / WAIS / next-ui / BrowserPane.m < prev    next >
Encoding:
Text File  |  1992-07-16  |  22.3 KB  |  907 lines

  1. // BrowserPane.m
  2. //
  3. // Free software created 1 Feb 1992
  4. // by Paul Burchard <burchard@math.utah.edu>.
  5.  
  6. #import "BrowserPane.h"
  7. #import <objc/Storage.h>
  8. #import <appkit/appkit.h>
  9. #import <string.h>
  10. #import <sys/types.h>
  11. #import <sys/stat.h>
  12. #import <sys/file.h>
  13.  
  14. @implementation BrowserPane
  15.  
  16.  
  17. // Init, freeing, archiving.
  18.  
  19. - initFrame:(const NXRect *)frameRect
  20. {
  21.     return [self initFrame:frameRect cellClass:[TextFieldCell class]];
  22. }
  23.  
  24. - initFrame:(const NXRect *)frameRect cellClass:factoryId
  25. {
  26.     id matrix;
  27.     NXRect subframe;
  28.     NXSize cellsize;
  29.     
  30.     [super initFrame:frameRect];
  31.     [self setBorderType:NX_BEZEL];
  32.     [self setBackgroundGray:NX_LTGRAY];
  33.     [self setVertScrollerRequired:YES];
  34.     [self setAutoresizeSubviews:YES];
  35.     action = @selector(takeStringValueFrom:);
  36.     doubleAction = @selector(takeStringValueFrom:);
  37.     stringValue = [[Storage alloc] initCount:0 elementSize:sizeof(char) description:"c"];
  38.     stringList = [[List alloc] initCount:0];
  39.     separator = '\t';
  40.     
  41.     // Create Matrix of full-width Cells of class factoryId,
  42.     // and place in our scrollView as its docView.
  43.     [contentView getFrame:&subframe];
  44.     matrix = [[Matrix alloc] initFrame:&subframe mode:NX_LISTMODE cellClass:factoryId numRows:0 numCols:1];
  45.     if(!matrix) return nil;
  46.     cellsize.width = cellsize.height = 0.0;
  47.     [matrix setIntercell:&cellsize];
  48.     [matrix getCellSize:&cellsize];
  49.     cellsize.width = NX_WIDTH(&subframe);
  50.     [matrix setCellSize:&cellsize];
  51.     [matrix setAutosizeCells:YES];
  52.     [matrix setAutoscroll:YES];
  53.     [[matrix setAction:@selector(sendAction)] setTarget:self];
  54.     [matrix setDoubleAction:@selector(sendDoubleAction)];
  55.     [self setDocView:matrix];
  56.     [matrix sizeToCells];
  57.     [self setAutodisplay:YES];
  58.     [self setNeedsDisplay:YES];
  59.     
  60.     return self;
  61. }
  62.  
  63. - resizeSubviews:(const NXSize *)oldSize
  64. {
  65.     NXRect contentFrame, docFrame;
  66.  
  67.     [super resizeSubviews:oldSize];
  68.     
  69.     // Keep matrix at full width of contentView when size changes.
  70.     [contentView getFrame:&contentFrame];
  71.     [contentView convertRect:&contentFrame toView:contentView];//paranoia
  72.     [[self docView] getFrame:&docFrame];
  73.     [[self docView] sizeTo:NX_WIDTH(&contentFrame) :NX_HEIGHT(&docFrame)];
  74.     return self;
  75. }
  76.  
  77. - read:(NXTypedStream *)stream
  78. {
  79.     [super read:stream];
  80.     stringValue = NXReadObject(stream);
  81.     stringList = NXReadObject(stream);
  82.     delegate = NXReadObject(stream);
  83.     target = NXReadObject(stream);
  84.     // Assumes BOOL is size of char.
  85.     NXReadTypes(stream, "::ccccc", &action, &doubleAction,
  86.         &isAlphabetized, &isAbbreviated, &isEditable, &isDisabledOnEntry,
  87.     &separator);
  88.     return self;
  89. }
  90.  
  91. - write:(NXTypedStream *)stream
  92. {
  93.     [super write:stream];
  94.     NXWriteObject(stream, stringValue);
  95.     NXWriteObject(stream, stringList);
  96.     NXWriteObjectReference(stream, delegate);
  97.     NXWriteObjectReference(stream, target);
  98.     // Assumes BOOL is size of char.
  99.     NXWriteTypes(stream, "::ccccc", &action, &doubleAction,
  100.         &isAlphabetized, &isAbbreviated, &isEditable, &isDisabledOnEntry,
  101.     &separator);
  102.     return self;
  103. }
  104.  
  105. - free
  106. {
  107.     [stringValue free];
  108.     [[stringList freeObjects] free];
  109.     return [super free];
  110. }
  111.  
  112.  
  113. // Adding and removing entries.
  114.  
  115. // Internal use only.
  116. - (int)indexAddEntryStorage:stringStorage
  117. {
  118.     int row, nrows, ncols, len;
  119.     id matrix;
  120.     char *newName, *abbrevName = 0;
  121.     const char *name;
  122.     
  123.     // Don't enter again if already in here.
  124.     if(!stringStorage) return (-1);
  125.     if(!(newName = [stringStorage elementAt:0])) return (-1);
  126.     if((row=[self indexOfEntry:newName]) >= 0)
  127.         { [stringStorage free]; return row; }
  128.     
  129.     // Get abbreviation if necessary.
  130.     if(isAbbreviated)
  131.     {
  132.     len = strlen(newName);
  133.     [stringStorage setNumSlots:2*(len+1)];
  134.     abbrevName = (char *)[stringStorage elementAt:(len+1)];
  135.     newName = (char *)[stringStorage elementAt:0];
  136.     if(!abbrevName || !newName) return (-1);
  137.     *abbrevName = 0;
  138.     [self abbreviate:newName to:abbrevName];
  139.     }
  140.     
  141.     // Find slot for new entry (sort by abbreviated forms).
  142.     matrix = [self docView];
  143.     [matrix getNumRows:&nrows numCols:&ncols];
  144.     if(isAlphabetized) for(row=0; row<nrows; row++)
  145.     {
  146.         name = [[matrix cellAt:row :0] stringValue];
  147.     if(isAbbreviated && [self isFirst:abbrevName second:name]) break;
  148.     if(!isAbbreviated && [self isFirst:newName second:name]) break;
  149.     }
  150.     else row = nrows;
  151.     
  152.     // Insert new entry into matrix and string list, disabling if req'd.
  153.     [matrix insertRowAt:row];
  154.     [matrix sizeToCells];
  155.     [[matrix cellAt:row :0]
  156.         setStringValue:(isAbbreviated ? abbrevName : newName)];
  157.     [stringList insertObject:stringStorage at:row];
  158.     if(isDisabledOnEntry) [self setEntryEnabled:NO at:row];
  159.     return row;    
  160. }
  161.  
  162. - (int)indexAddEntry:(const char *)aString
  163. {
  164.     int row, len;
  165.     id stringStorage;
  166.     
  167.     if(!aString) return (-1);
  168.     len = strlen(aString);
  169.     stringStorage = [[Storage alloc] initCount:(len+1) elementSize:sizeof(char) description:"c"];
  170.     [stringStorage setNumSlots:(len+1)];
  171.     strcpy((char *)[stringStorage elementAt:0], aString);
  172.     row = [self indexAddEntryStorage:stringStorage];
  173.     if(row < 0) { [stringStorage free]; return (-1); }
  174.     return row;
  175. }
  176.  
  177. - addEntry:(const char *)aString
  178. {
  179.     if([self indexAddEntry:aString] < 0) return nil;
  180.     else return self;
  181. }
  182.  
  183. - (const char *)entryAt:(int)row
  184. {
  185.     return (const char *)[[stringList objectAt:row] elementAt:0];
  186. }
  187.  
  188. - cellAt:(int)row
  189. {
  190.     return [[self docView] cellAt:row :0];
  191. }
  192.  
  193. - (int)indexOfEntry:(const char *)aString
  194. {
  195.     int row, nrows, ncols;
  196.     id matrix;
  197.     
  198.     if(!aString) return (-1);
  199.     matrix = [self docView];
  200.     [matrix getNumRows:&nrows numCols:&ncols];
  201.     for(row=0; row<nrows; row++)
  202.     if(strcmp([self entryAt:row], aString) == 0) break;
  203.     if(row >= nrows) return (-1);
  204.     return row;
  205. }
  206.  
  207. - (unsigned)count
  208. {
  209.     int nrows = (-1), ncols = (-1);
  210.     
  211.     [[self docView] getNumRows:&nrows numCols:&ncols];
  212.     if(nrows < 0) return 0;
  213.     else return nrows;
  214. }
  215.  
  216. - removeEntryAt:(int)row
  217. {
  218.     int nrows, ncols;
  219.     id matrix;
  220.     
  221.     // Removing a selected entry clears the selection.
  222.     matrix = [self docView];
  223.     if([self isEntrySelectedAt:row]) [self clearSelection];
  224.  
  225.     // Remove entry and resize Matrix.
  226.     [matrix getNumRows:&nrows numCols:&ncols];
  227.     if(row<0 || row>=nrows) return nil;
  228.     [matrix removeRowAt:row andFree:YES];
  229.     [matrix sizeToCells];
  230.     [[stringList removeObjectAt:row] free];
  231.     return self;
  232. }
  233.  
  234. - removeSelection
  235. {
  236.     int row, nrows, ncols;
  237.     id matrix;
  238.     
  239.     // Remove selected entries (starting from last) and resize Matrix.
  240.     matrix = [self docView];
  241.     [matrix getNumRows:&nrows numCols:&ncols];
  242.     for(row=nrows-1; row>=0; row--) if([self isEntrySelectedAt:row])
  243.     {
  244.     [matrix removeRowAt:row andFree:YES];
  245.     [[stringList removeObjectAt:row] free];
  246.     }
  247.     [matrix sizeToCells];
  248.     [self clearSelection];
  249.     return self;
  250. }
  251.  
  252. - clear
  253. {
  254.     int row, nrows, ncols;
  255.     id matrix;
  256.     
  257.     [self clearSelection];
  258.     matrix = [self docView];
  259.     [matrix getNumRows:&nrows numCols:&ncols];
  260.     for(row=nrows-1; row>=0; row--)
  261.     {
  262.         [matrix removeRowAt:row andFree:YES];
  263.     [[stringList removeObjectAt:row] free];
  264.     }
  265.     [matrix sizeToCells];
  266.     return self;
  267. }
  268.  
  269. - addFiles:(const char *)dir suffix:(const char *)sfx;
  270. {
  271.     char command[2048], *fgets(), *name;
  272.     FILE *pipe;
  273.     struct stat statbuf;
  274.     
  275.     sprintf(command, "/bin/ls -1d 2>/dev/null %s/*", dir);
  276.     if(sfx != NULL) { strcat(command, "."); strcat(command, sfx); }
  277.     pipe = popen(command, "r");
  278.     while(fgets(command, 2048, pipe) != NULL)
  279.     {
  280.     command[strlen (command) - 1] = '\0';
  281.     stat(command, &statbuf);
  282.     if((name = strrchr(command, '/')) != NULL) name++;
  283.     else name = command;
  284.     if(!(statbuf.st_mode & S_IFDIR)) [self addEntry:name];
  285.     }
  286.     pclose(pipe);
  287.     return self;
  288. }
  289.  
  290.  
  291. // Adding and selecting entries via stringValue.
  292.  
  293. - (const char *)grabStringFrom:sender
  294. {
  295.     if([sender respondsTo:@selector(stringValue)])
  296.         return [sender stringValue];
  297.     else if([sender respondsTo:@selector(stringValueAt:)])
  298.         return [sender stringValueAt:0];
  299.     else return (const char *)0;
  300. }
  301.  
  302. - (const char *)stringValue
  303. {
  304.     if([stringValue count] <= 0) return 0;
  305.     return (const char *)[stringValue elementAt:0];
  306. }
  307.  
  308. - setStringValue:(const char *)aString
  309. {
  310.     return [self setStringValue:aString append:NO];
  311. }
  312.  
  313. - setStringValue:(const char *)aString append:(BOOL)yn
  314. {
  315.     int row, len;
  316.     const char *nextString;
  317.     char *endOfEntry;
  318.     id nextEntry;
  319.  
  320.     // Clear selection first if not appending to stringValue.
  321.     if(!yn || !aString) [self clearSelection];
  322.     if(!aString) return self;
  323.     
  324.     // Add TAB-separated items to selection.
  325.     // If not already in matrix and list, add them.
  326.     for(nextString=aString; nextString;
  327.         nextString=strchr(nextString, separator))
  328.     {
  329.         // Create Storage string to hold unabbrev next entry.
  330.         if(*nextString == separator) nextString++;
  331.     len = strlen(nextString);
  332.     nextEntry = [[Storage alloc] initCount:(len+1) elementSize:sizeof(char) description:"c"];
  333.     [nextEntry setNumSlots:(len+1)];
  334.     strcpy((char *)[nextEntry elementAt:0], nextString);
  335.     endOfEntry = strchr((char *)[nextEntry elementAt:0], separator);
  336.     if(endOfEntry) *endOfEntry = 0;
  337.     
  338.     // Add to matrix and list if necessary, disabled if req'd.
  339.     row = [self indexAddEntryStorage:nextEntry];
  340.     if(row < 0) { [nextEntry free]; return nil; }
  341.     if(isDisabledOnEntry) [self setEntryEnabled:NO at:row];
  342.     
  343.     // Add entry to selection.
  344.     if(!isDisabledOnEntry) [self selectEntryAt:row append:YES];
  345.     }
  346.     [self update];
  347.     return self;
  348. }
  349.  
  350. - takeStringValueFrom:sender
  351. {
  352.     id oldTarget = nil;
  353.     id rtn;
  354.     
  355.     // If sender is target, don't send action (to avoid circularity).
  356.     if(sender == target) { oldTarget = target; [self setTarget:nil]; }
  357.     rtn = [self setStringValue:[self grabStringFrom:sender] append:NO];
  358.     if(oldTarget) [self setTarget:oldTarget];
  359.     
  360.     // Notify delegate of user-induced change in list.
  361.     // (Actually, due to non-duplication no change may have taken place.)
  362.     if([delegate respondsTo:@selector(textDidChange:)])
  363.         [delegate textDidChange:self];
  364.     return rtn;
  365. }
  366.  
  367. - appendStringValueFrom:sender
  368. {
  369.     id oldTarget = nil;
  370.     id rtn;
  371.     
  372.     // If sender is target, don't send action (to avoid circularity).
  373.     if(sender == target) { oldTarget = target; [self setTarget:nil]; }
  374.     rtn = [self setStringValue:[self grabStringFrom:sender] append:YES];
  375.     if(oldTarget) [self setTarget:oldTarget];
  376.     
  377.     // Notify delegate of user-induced change in list.
  378.     // (Actually, due to non-duplication no change may have taken place.)
  379.     if([delegate respondsTo:@selector(textDidChange:)])
  380.         [delegate textDidChange:self];
  381.     return rtn;
  382. }
  383.  
  384. - (char)separator
  385. {
  386.     return separator;
  387. }
  388.  
  389. - setSeparator:(char)c
  390. {
  391.     if(!c) return nil;
  392.     separator = c;
  393.     return self;
  394. }
  395.  
  396.  
  397. // Selecting.
  398.  
  399. - selectEntryAt:(int)row append:(BOOL)yn
  400. {
  401.     int nrows, ncols, oldlen;
  402.     const char *entryString;
  403.     id matrix;
  404.     
  405.     matrix = [self docView];
  406.     [matrix getNumRows:&nrows numCols:&ncols];
  407.     if(row<0 || row>=nrows) return nil;
  408.     if(![self isEntryEnabledAt:row]) return nil;
  409.     entryString = (char *)[[stringList objectAt:row] elementAt:0];
  410.     
  411.     if(yn)
  412.     {
  413.         // Append to selection.
  414.     // Selection indicated by cell state and highlighting.
  415.     [matrix lockFocus];
  416.     [matrix highlightCellAt:row :0 lit:YES];
  417.     [matrix setState:1 at:row :0];
  418.     [matrix unlockFocus];
  419.     if([stringValue count] <= 0) oldlen = 0;
  420.     else oldlen = strlen((char *)[stringValue elementAt:0]);
  421.     [stringValue setNumSlots:(oldlen + 1 + strlen(entryString) + 1)];
  422.     sprintf((char *)[stringValue elementAt:0] + oldlen, 
  423.         (oldlen>0 ? "\t%s" : "%s"), entryString);
  424.     }
  425.     else
  426.     {
  427.         // Create new selection.
  428.     // Selection indicated by cell state and highlighting.
  429.     [self clearSelection];
  430.     [matrix lockFocus];
  431.     [matrix highlightCellAt:row :0 lit:YES];
  432.     [matrix setState:1 at:row :0];
  433.     [matrix unlockFocus];
  434.     [stringValue setNumSlots:(strlen(entryString)+1)];
  435.     strcpy((char *)[stringValue elementAt:0], entryString);
  436.     }
  437.     
  438.     // Notify target of new selection.
  439.     if(action) [target perform:action with:self];
  440.     return self;
  441. }
  442.  
  443. - (BOOL)isEntrySelectedAt:(int)row
  444. {
  445.     int nrows, ncols;
  446.     id matrix;
  447.     
  448.     // Selection indicated by cell state and highlighting.
  449.     // Make sure state is consistent with highlighting and enabling.
  450.     matrix = [self docView];
  451.     [matrix getNumRows:&nrows numCols:&ncols];
  452.     if(row<0 || row>=nrows) return NO;
  453.     if(![self isEntryEnabledAt:row] || ![[matrix cellAt:row :0] isHighlighted])
  454.     {
  455.     [matrix setState:0 at:row :0];
  456.     return NO;
  457.     }
  458.     [matrix setState:1 at:row :0];
  459.     return YES;
  460. }
  461.  
  462. - clearSelection
  463. {
  464.     int row, nrows, ncols;
  465.     id matrix;
  466.     
  467.     matrix = [self docView];
  468.     [matrix selectCellAt:(-1) :(-1)];
  469.     [matrix getNumRows:&nrows numCols:&ncols];
  470.     [matrix lockFocus];
  471.     for(row=0; row<nrows; row++)
  472.     {
  473.         // Selection indicated by cell state and highlighting.
  474.         [matrix highlightCellAt:row :0 lit:NO];
  475.     [matrix setState:0 at:row :0];
  476.     }
  477.     [matrix unlockFocus];
  478.     [stringValue setNumSlots:0];
  479.     if(action) [target perform:action with:self];
  480.     return self;
  481. }
  482.  
  483.  
  484. // Target and action.
  485.  
  486. // For internal use only.
  487. - (BOOL)appendSelStringFrom:sender
  488. {
  489.     int row, oldlen;
  490.     const char *nextString;
  491.     
  492.     // Find (enabled) cell and its unabbreviated stringValue.
  493.     if((row=[[[self docView] cellList] indexOf:sender]) == NX_NOT_IN_LIST)
  494.         return NO;
  495.     if(![self isEntryEnabledAt:row]) return YES; // continue looping thru cells
  496.     nextString = (char *)[[stringList objectAt:row] elementAt:0];
  497.     
  498.     // Selection indicated by cell state and highlighting.
  499.     // Make sure state is consistent with highlighting.
  500.     if(![sender isHighlighted])
  501.     {
  502.     [sender setState:0];
  503.     return YES; // continue looping thru cells
  504.     }
  505.     [sender setState:1];
  506.  
  507.     // Append sender's unabbrev stringValue onto ours (with TAB separation).
  508.     if([stringValue count] <= 0) oldlen = 0;
  509.     else oldlen = strlen((char *)[stringValue elementAt:0]);
  510.     [stringValue setNumSlots:(oldlen + 1 + strlen(nextString) + 1)];
  511.     sprintf((char *)[stringValue elementAt:0] + oldlen, 
  512.     (oldlen>0 ? "\t%s" : "%s"), nextString);
  513.     return YES; // continue looping thru cells
  514. }
  515.  
  516. // For internal use only.
  517. - recomputeStringValueFromSelection
  518. {
  519.     [stringValue setNumSlots:0];
  520.     [[self docView] sendAction:@selector(appendSelStringFrom:) to:self forAllCells:NO];
  521.     return self;
  522. }
  523.  
  524. - sendAction
  525. {
  526.     // Sent by newly selected cells in matrix.
  527.     // So recompute stringValue and make self firstResponder.
  528.     [self recomputeStringValueFromSelection];
  529.     [window makeFirstResponder:self];
  530.     if(!action) return self;
  531.     return [target perform:action with:self];
  532. }
  533.  
  534. - sendDoubleAction
  535. {
  536.     char *fileName, *nxt;
  537.     int ok;
  538.     
  539.     // Sent by newly selected cell in matrix.
  540.     // So recompute stringValue and make self firstResponder.
  541.     [self recomputeStringValueFromSelection];
  542.     [window makeFirstResponder:self];
  543.     
  544.     // Double-click means open files (if delegate exists and opens files).
  545.     // Open files one by one (there may be many if msg sent by outside obj).
  546.     if([stringValue count]>0 && [delegate respondsTo:@selector(openFile:ok:)])
  547.     {
  548.     fileName = (char *)[stringValue elementAt:0];
  549.     nxt = strchr(fileName, separator);
  550.     while(fileName)
  551.     {
  552.             if(nxt) *nxt = 0;
  553.         [delegate openFile:fileName ok:&ok];
  554.         if(nxt)
  555.         {
  556.         *nxt = separator;
  557.             fileName = nxt+1;
  558.         nxt = strchr(fileName, separator);
  559.         }
  560.         else fileName = nxt = 0;
  561.     }
  562.     }
  563.     
  564.     // Send doubleAction to target.
  565.     if(!doubleAction) return self;
  566.     return [target perform:doubleAction with:self];
  567. }
  568.  
  569. - setTarget:anObject
  570. {
  571.     target = anObject;
  572.     [[self docView] setTarget:self];
  573.     return self;
  574. }
  575.  
  576. - setAction:(SEL)aSelector
  577. {
  578.     action = aSelector;
  579.     [[self docView] setAction:@selector(sendAction)];
  580.     return self;
  581. }
  582.  
  583. - setDoubleAction:(SEL)aSelector
  584. {
  585.     doubleAction = aSelector;
  586.     [[self docView] setDoubleAction:@selector(sendDoubleAction)];
  587.     return self;
  588. }
  589.  
  590. - target
  591. {
  592.     return target;
  593. }
  594.  
  595. - (SEL)action
  596. {
  597.     return action;
  598. }
  599.  
  600. - (SEL)doubleAction
  601. {
  602.     return doubleAction;
  603. }
  604.  
  605.  
  606. // Editing.
  607.  
  608. - setEditable:(BOOL)yn
  609. {
  610.     isEditable = yn;
  611.     return self;
  612. }
  613.  
  614. - (BOOL)isEditable
  615. {
  616.     return isEditable;
  617. }
  618.  
  619. - (BOOL)acceptsFirstResponder
  620. {
  621.     return YES;
  622. }
  623.  
  624. - becomeFirstResponder
  625. {
  626.     [self setBackgroundGray:NX_WHITE];
  627.     [self update];
  628.     return self;
  629. }
  630.  
  631. - resignFirstResponder
  632. {
  633.     [self setBackgroundGray:NX_LTGRAY];
  634.     [self update];
  635.     return self;
  636. }
  637.  
  638. - keyDown:(NXEvent *)theEvent
  639. {
  640.     if(theEvent->type!=NX_KEYDOWN) return self;
  641.     if(!isEditable) return nil;
  642.     switch(theEvent->data.key.charCode)
  643.     {
  644.     case NX_DELETE:
  645.         [self delete:self];
  646.     break;
  647.     default:
  648.         return nil;
  649.     }        
  650.     return self;
  651. }
  652.  
  653. - mouseDown:(NXEvent *)theEvent
  654. {
  655.     // In case matrix is empty or does not fill view, capture mouse clicks.
  656.     [super mouseDown:theEvent];
  657.     if([window firstResponder] != self) [window makeFirstResponder:self];
  658.     return self;
  659. }
  660.  
  661. - delete:sender
  662. {
  663.     char *fileName, *nxt;
  664.     int ok;
  665.     
  666.     if(!isEditable) return self;
  667.  
  668.     // Let delegate delete selected "files" if it can.
  669.     if([stringValue count]>0
  670.         && [delegate respondsTo:@selector(removeFile:ok:)])
  671.     {
  672.     fileName = (char *)[stringValue elementAt:0];
  673.     nxt = strchr(fileName, separator);
  674.     while(fileName)
  675.     {
  676.             if(nxt) *nxt = 0;
  677.         [delegate removeFile:fileName ok:&ok];
  678.         if(nxt)
  679.         {
  680.         *nxt = separator;
  681.             fileName = nxt+1;
  682.         nxt = strchr(fileName, separator);
  683.         }
  684.         else fileName = nxt = 0;
  685.     }
  686.     }
  687.  
  688.     // Now remove selected names from list.
  689.     [self removeSelection];
  690.     [self update];
  691.     
  692.     // Notify delegate of user-induced change in list.
  693.     if([delegate respondsTo:@selector(textDidChange:)])
  694.         [delegate textDidChange:self];
  695.     return self;
  696. }
  697.  
  698. - cut:sender
  699. {
  700.     const char *sval;
  701.     
  702.     if(!isEditable) return self;
  703.     
  704.     // Copy out string value...
  705.     if(!(sval = [self stringValue])) return self;
  706.     [[Pasteboard new] declareTypes:&NXAsciiPboard num:1 owner:self];
  707.     [[Pasteboard new] writeType:NXAsciiPboard data:sval length:strlen(sval)];
  708.     
  709.     // Delete selection (and associated "files" if any).
  710.     return [self delete:sender];
  711. }
  712.  
  713. - copy:sender
  714. {
  715.     char *fileName, *nxt;
  716.     int ok;
  717.     const char *sval;
  718.     
  719.     if(!(sval = [self stringValue])) return self;
  720.  
  721.     // Let delegate "prepare" selected "files" if it can.
  722.     if([delegate respondsTo:@selector(prepFile:ok:)])
  723.     {
  724.     fileName = sval;
  725.     nxt = strchr(fileName, separator);
  726.     while(fileName)
  727.     {
  728.             if(nxt) *nxt = 0;
  729.         [delegate prepFile:fileName ok:&ok];
  730.         if(nxt)
  731.         {
  732.         *nxt = separator;
  733.             fileName = nxt+1;
  734.         nxt = strchr(fileName, separator);
  735.         }
  736.         else fileName = nxt = 0;
  737.     }
  738.     }
  739.  
  740.     // Copy out string value.
  741.     [[Pasteboard new] declareTypes:&NXAsciiPboard num:1 owner:self];
  742.     [[Pasteboard new] writeType:NXAsciiPboard data:sval length:strlen(sval)];
  743.     [self update];
  744.     return self;
  745. }
  746.  
  747. - paste:sender
  748. {
  749.     char *sval;
  750.     int length;
  751.     
  752.     if(!isEditable) return self;
  753.     [[Pasteboard new] readType:NXAsciiPboard data:&sval length:&length];
  754.     [self setStringValue:sval]; //!!! is the data null-terminated?
  755.     vm_deallocate(task_self(), (vm_address_t)sval, sizeof(char)*length);
  756.     [self update];
  757.  
  758.     // Notify delegate of user-induced change in list.
  759.     if([delegate respondsTo:@selector(textDidChange:)])
  760.         [delegate textDidChange:self];
  761.     return self;
  762. }
  763.  
  764. - selectAll:sender
  765. {
  766.     int row, nrows, ncols;
  767.     id matrix;
  768.     BOOL autodisplay;
  769.     
  770.     //!!! Hideous in action (autoDisplay isn't getting shut off?).
  771.     matrix = [self docView];
  772.     autodisplay = [matrix isAutodisplay];
  773.     [matrix setAutodisplay:NO];
  774.     [matrix getNumRows:&nrows numCols:&ncols];
  775.     for(row=0; row<nrows; row++) [self selectEntryAt:row append:YES];
  776.     [matrix setAutodisplay:autodisplay];
  777.     [self update];
  778.     return self;
  779. }
  780.  
  781. - (int)openFile:(const char *)fileName ok:(int *)flag
  782. {
  783.     // Default does nothing.
  784.     return 0;
  785. }
  786.  
  787. - (int)prepFile:(const char *)fileName ok:(int *)flag
  788. {
  789.     // Default does nothing.
  790.     return 0;
  791. }
  792.  
  793. - (int)removeFile:(const char *)fileName ok:(int *)flag
  794. {
  795.     // Default does nothing.
  796.     return 0;
  797. }
  798.  
  799. - textDidChange:sender
  800. {
  801.     return self;
  802. }
  803.  
  804.  
  805. // String display control.
  806.  
  807. - setEntryEnabled:(BOOL)yn at:(int)row
  808. {
  809.     id matrix, cell;
  810.     
  811.     // Disabling a selected entry clears the selection.
  812.     if(!yn && [self isEntrySelectedAt:row]) [self clearSelection];
  813.     matrix = [self docView]; cell = [matrix cellAt:row :0];
  814.     [cell setEnabled:yn];
  815.     //!!! Note: textGray seems to be relative to the background??
  816.     if(yn) [matrix drawCell:[cell setTextGray:NX_BLACK]];
  817.     else [matrix drawCell:[cell setTextGray:NX_LTGRAY]];
  818.     return self;
  819. }
  820.  
  821. - (BOOL)isEntryEnabledAt:(int)row
  822. {
  823.     return [[[self docView] cellAt:row :0] isEnabled];
  824. }
  825.  
  826. - setDisabledOnEntry:(BOOL)yn
  827. {
  828.     isDisabledOnEntry = yn;
  829.     return self;
  830. }
  831.  
  832. - (BOOL)isDisabledOnEntry
  833. {
  834.     return isDisabledOnEntry;
  835. }
  836.  
  837. - setAlphabetized:(BOOL)yn
  838. {
  839.     //!!! Does not retroactively sort yet! Sorry!
  840.     isAlphabetized = yn;
  841.     return self;
  842. }
  843.  
  844. - (BOOL)isAlphabetized
  845. {
  846.     return isAlphabetized;
  847. }
  848.  
  849. - (BOOL)isFirst:(const char *)firstString second:(const char *)secondString
  850. {
  851.     // Standard NeXTstep string comparison.
  852.     if(!secondString || !firstString) return NO;
  853.     if(NXOrderStrings(secondString, firstString, NO, -1, NULL) > 0)
  854.         return YES;
  855.     else return NO;
  856. }
  857.  
  858. - setAbbreviated:(BOOL)yn
  859. {
  860.     int len, row, nrows, ncols;
  861.     char *fullName, *abbrevName;
  862.     id matrix, stringStorage;
  863.     
  864.     isAbbreviated = yn;
  865.     matrix = [self docView];
  866.     [matrix getNumRows:&nrows numCols:&ncols];
  867.     for(row=0; row<nrows; row++)
  868.     {
  869.         stringStorage = [stringList objectAt:row];
  870.         if(!(fullName = (char *)[stringStorage elementAt:0])) continue;
  871.     len = strlen(fullName);
  872.     [stringStorage setNumSlots:2*(len+1)];
  873.     abbrevName = (char *)[stringStorage elementAt:(len+1)];
  874.     fullName = (char *)[stringStorage elementAt:0];
  875.     if(!abbrevName || !fullName) continue;
  876.     *abbrevName = 0;
  877.     [self abbreviate:fullName to:abbrevName];
  878.     [[matrix cellAt:row :0] setStringValue:abbrevName];
  879.     }
  880.     [self update];
  881.     return self;
  882. }
  883.  
  884. - (BOOL)isAbbreviated
  885. {
  886.     return isAbbreviated;
  887. }
  888.  
  889. - abbreviate:(const char *)srcString to:(char *)destString
  890. {
  891.     const char *abbr;
  892.     
  893.     // Show part after last '/'.
  894.     if(!destString) return nil;
  895.     if(!srcString) { *destString = 0; return self; }
  896.     abbr = strrchr(srcString, '/');
  897.     if(!abbr) abbr = srcString;
  898.     else abbr++;
  899.     strcpy(destString, abbr);
  900.     return self;
  901. }
  902.  
  903. @end
  904.  
  905.  
  906.  
  907.